﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lexer;
using RPGProject.GameWorld.GameObjects;
using RPGProject.IOBuffers;
using RPGProject.Data_Structures;
using RPGProject.LoopSystem;
namespace RPGProject.GameWorld
{
    /// <summary>
    /// For sending out property updates.
    /// </summary>
    /// <param name="name"></param>
    /// <param name="value"></param>
    public delegate void OnPropUpdate(String name, Object value);
    /// <summary>
    /// Deals with managing everything in a world.
    /// </summary>
    sealed partial class World
    {
        private GameObject[][] activeLevel;
        private Dictionary<String, GameObject> activeObjects;
        private Dictionary<String, SpawnPoint> spawnPoints;
        private MapTile[][] backgroundLevel;
        private OutputBuffer outBuffer;
        private LogOutputBuffer smallLogBuffer;
        private LogOutputBuffer bigLogBuffer;
        private StatOutputBuffer stats;
        private Screen screen;
        private Dictionary<String, Object> properties;
        private Vector<ScriptRunner> scripts;
        private Vector<OnPropUpdate> onUpdates;
        private Visuals visuals;
        private WorldFrameLooper wfl;
        private WorldTurnLooper wtl;
        /// <summary>
        /// Creates a new world.
        /// </summary>
        /// <param name="width">Width of the world.</param>
        /// <param name="height">Height of the world.</param>
        /// <param name="statHeight">Height of the stat bar.</param>
        /// <param name="bufferHeight">How much space to allocate to the log.</param>
        public World(int width, int height, int statHeight, int bufferHeight)
            : base(new LoopManager())
        {
            this.properties = new Dictionary<String, Object>();
            this.onUpdates = new Vector<OnPropUpdate>();
            // init important data
            this["enemycount"] = 0;
            this["width"] = width;
            this["height"] = height;
            this["turn"] = 0;
            this["win"] = false;
            this["loss"] = false;
            this.activeLevel = new GameObject[(int)this["width"]][];
            this.spawnPoints = new Dictionary<String, SpawnPoint>();
            this.activeObjects = new Dictionary<String, GameObject>();
            this.backgroundLevel = new MapTile[(int)this["width"]][];
            // populate the world with blanktiles.
            for (int i = 0; i < (int)this["width"]; i++)
            {
                this.activeLevel[i] = new GameObject[(int)this["height"]];
                this.backgroundLevel[i] = new MapTile[(int)this["height"]];
                for (int j = 0; j < (int)this["height"]; j++)
                {
                    this.activeLevel[i][j] = new NullGameObject(i, j);
                    this.backgroundLevel[i][j] = new MapTile(i, j);
                    this.backgroundLevel[i][j].background = ConsoleColor.Black;
                }
            }
            // init buffers
            this.outBuffer = new OutputBuffer((int)this["width"], (int)this["height"], new CharInfo(' '), false);
            this.smallLogBuffer = new LogOutputBuffer((int)this["width"], bufferHeight);
            this.bigLogBuffer = new LogOutputBuffer((int)this["width"], (int)this["height"] + bufferHeight, true);
            this.stats = new StatOutputBuffer((int)this["width"], statHeight);
            this.onUpdates.Add(this.stats.OnPropUpdate);
            this.screen = new Screen((int)this["width"], (int)this["height"] + bufferHeight + statHeight);
            this.screen.AllocateScreen(0, 0, this.stats);
            this.screen.AllocateScreen(0, statHeight, this.outBuffer);
            this.screen.AllocateScreen(0, (int)this["height"] + statHeight, this.smallLogBuffer);
            this.scripts = new Vector<ScriptRunner>();

            this.visuals = new Visuals(width, height, this, this.loopManager);
            this.wfl = new WorldFrameLooper(this, this.loopManager);
            this.wtl = new WorldTurnLooper(this, this.loopManager);
            this.loopManager.AddFunc(FuncType.ADD_TO_END, this.wtl);
            this.loopManager.CycleTo = this.wfl;
            this["end"] = false;
        }

    }
}
